/**
 *
 */
package com.wedroid.r2d2.resource;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.utils.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.wedroid.r2d2.contact.Contact;
import com.wedroid.r2d2.http.RequestManager;

/**
 * @author lute
 */
@Component
public class ResourceManager {

    private final Logger logger = LoggerFactory.getLogger(ResourceManager.class);

    @Value("#{config['resource.path']}")
    private String resourcePath;

    @Autowired
    private RequestManager requestManager;

    /**
     * Download image message resource
     *
     * @param msgId
     * @param messageTime
     * @param senderName
     * @return
     */
    public String downloadImage(String msgId, Date messageTime, String senderName) {
        return downloadImage(msgId, messageTime, senderName, false);
    }

    /**
     * @param msgId
     * @param messageTime
     * @param senderName
     * @return
     */
    public String downloadSlaveImage(String msgId, Date messageTime, String senderName) {
        return downloadImage(msgId, messageTime, senderName, true);
    }

    /**
     * @param msgId
     * @param messageTime
     * @param senderName
     * @param isSlave
     * @return
     */
    private String downloadImage(String msgId, Date messageTime, String senderName, boolean isSlave) {
        ResourceType resourceType = ResourceType.IMAGE;
        String path = ensureResourcePath(resourceType, messageTime, senderName);
        CloseableHttpResponse response = requestManager.getImageMessage(msgId, isSlave);
        return saveResource(response, path, msgId);
    }

    /**
     * Download voice message resource
     *
     * @param msgId
     * @param messageTime
     * @param senderName
     * @return
     */
    public String downloadVoice(String msgId, Date messageTime, String senderName) {
        ResourceType resourceType = ResourceType.VOICE;
        String path = ensureResourcePath(resourceType, messageTime, senderName);
        CloseableHttpResponse response = requestManager.getVoiceMessage(msgId);
        return saveResource(response, path, msgId);
    }

    /**
     * @param contact
     * @return
     * @see #requestAvatar(Contact, String)
     */
    public String requestAvatar(Contact contact) {
        return requestAvatar(contact, null);
    }

    /**
     * Download contact or group member head image
     *
     * @param contact
     * @param chatRoomId
     * @return
     */
    public String requestAvatar(Contact contact, String chatRoomId) {
        if (contact != null && StringUtils.isNoneBlank(contact.getUserName())) {
            String name = contact.getUserName().replace("@", "");
            String directory = ensureResourcePath(ResourceType.AVATAR, new Date(), name);

            CloseableHttpResponse response = contact.isGroup() ?
                    requestManager.getHeadImg(contact) : requestManager.getIcon(contact, chatRoomId);
            return saveResource(response, directory, name);
        } else {
            logger.warn("#requestAvatar - contact [{}] or contact user nmae [{}] is null.", contact, contact.getUserName());
            return null;
        }
    }


    /**
     * @param msgId
     * @param senderName
     * @param path
     * @return
     */
    public String downloadPublicLinkImg(String msgId, String senderName, String path) {
        String name = msgId;
        String resourcePath = ensureResourcePath(ResourceType.IMAGE, new Date(), senderName);
        CloseableHttpResponse response = requestManager.getPublicLinkImg(path);
        return saveResource(response, resourcePath, name);
    }

    /**
     * Construct message resource path and make necessary directories if needed
     *
     * @param type
     * @param time
     * @param name
     * @return
     */
    private String ensureResourcePath(ResourceType type, Date time, String name) {
        String path = new StringBuilder(resourcePath)
                            .append(File.separator).append(type.name().toLowerCase())
                            .append(File.separator).append(DateUtils.formatDate(time, "yyyyMMdd"))
                            .append(File.separator).append(name.replace("@", ""))
                            .toString();

        File directory = new File(path);
        try {
            directory.mkdirs();
            return path;
        } catch (Exception e) {
            logger.error("#ensureMessageResourcePath - Making directories [{}] encounters exception: {}", path, e);
        }
        return null;
    }

    /**
     * Save resource response stream as desired file
     *
     * @param response
     * @param path
     * @param name
     */
    private String saveResource(CloseableHttpResponse response, String path, String name) {
        if (response == null) {
            logger.error("#saveResource - Empty response.");
            return null;
        }

        if (StringUtils.isBlank(path) || StringUtils.isBlank(name)) {
            logger.error("#saveResource - Empty path [{}] or empty name [{}]", path, name);
            return null;
        }

        try {
            // Check content type
            Header header = response.getFirstHeader(HttpHeaders.CONTENT_TYPE);
            String contentType = decideResouceType(header);
            if (StringUtils.isBlank(contentType)) {
                logger.error("#saveResource - Response has no clear content type header [{}]", header);
                return null;
            }

            HttpEntity entity = response.getEntity();
            if (entity != null) {
                String resourcePath = new StringBuilder(path).append(File.separator)
                                                             .append(name.replace("@", ""))
                                                             .append(".")
                                                             .append(contentType)
                                                             .toString();
                File file = new File(resourcePath);
                InputStream inputStream = entity.getContent();
                try {
                    FileUtils.copyToFile(inputStream, file);
                } finally {
                    inputStream.close();
                }

                return resourcePath;
            }
        } catch (IOException e) {
            logger.error("#saveResource - Saving resource encounters IOException: {}", e);
        } catch (Exception e) {
            logger.error("#saveResource - Saving resource encounters Exception: {}", e);
        } finally {
            try {
                response.close();
            } catch (IOException e) {
            }
        }
        return null;
    }

    /**
     * @param header
     * @return
     */
    private String decideResouceType(Header header) {
        if (header != null) {
            String contentType = header.getValue();
            int slash = contentType.indexOf("/");
            if (slash != -1) {
                return contentType.substring(slash + 1);
            }
        }
        return null;
    }

    private enum ResourceType {
        IMAGE, VOICE, AVATAR, COMMON;
    }

}
